﻿using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace PetkaWebDav.Handler.AzureBlob
{
    public class CloudBlobManager
    {
        private String _connectionString = null;
        private CloudStorageAccount _storageAccount = null;
        private CloudBlobClient _cloudClient = null;
        public String _containerName = null;

        /// <summary>
        /// Creates Azure Storage Account with a given container name
        /// </summary>
        /// <param name="connectionString">Azure Blob Connection String</param>
        /// <param name="containerName">Azure Container Name</param>
        /// <param name="IsSecureContainer">True=private,False=public</param>
        public CloudBlobManager(String connectionString, String containerName, bool IsSecureContainer = false)
        {
            if (String.IsNullOrEmpty(connectionString))
                throw new ArgumentNullException("Azure Connection String can not be Null");

            if (String.IsNullOrEmpty(containerName))
                throw new ArgumentNullException("Azure Container Name String can not be Null");

            _connectionString = connectionString;

            _containerName = containerName;

            if (_storageAccount == null)
            {
                _storageAccount = CloudStorageAccount.Parse(_connectionString);
                _containerName = containerName;
                //we want our container to be secure
                CreateContainer(_containerName, true);
            }
        }

        #region Public Functions
        /// <summary>
        /// Downloads a blob
        /// </summary>
        /// <param name="containerName">Blob Cntainer Name</param>
        /// <param name="blobName">Blob Name</param>
        /// <returns>The contents of a blob as a MemoryStream</returns>
        public MemoryStream DownloadBlob(String blobName)
        {

            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName.ToLower());

            // Retrieve reference to a blob
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);

            var teamStream = new MemoryStream();

            using (MemoryStream memoryStream = new MemoryStream())
            {
                blockBlob.DownloadToStream(memoryStream);
                memoryStream.WriteTo(teamStream);
            }

            return teamStream;
        }

        /// <summary>
        /// Deletes a blob
        /// </summary>
        /// <param name="containerName">Blob Cntainer Name</param>
        /// <param name="blobName">Blob Name</param>
        public bool DeleteBlob(String blobName)
        {
            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName.ToLower());

            // Retrieve reference to a blob
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);

            return blockBlob.DeleteIfExists();
        }

        /// <summary>
        /// Uploads a file as blob
        /// </summary>
        /// <param name="containerName">Blob Cntainer Name</param>
        /// <param name="blobStream">Blob MemoryStream content</param>
        /// <param name="blobName">Blob Name</param>
        public string UploadBlob(MemoryStream blobStream, String blobName)
        {
            blobStream.Position = 0;
            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName.ToLower());

            // Retrieve reference to a blob
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);

            //Get MIME Type of the blob file based on the extension.
            string mime = System.Web.MimeMapping.GetMimeMapping(blobName);

            if (!string.IsNullOrEmpty(mime))
                blockBlob.Properties.ContentType = mime;

            // Create or overwrite the blob with contents from MemoryStream.
            blockBlob.UploadFromStream(blobStream);

            return blockBlob.Uri.ToString();
        }

        /// <summary>
        /// Retrieves Blobs Attributes
        /// </summary>
        /// <param name="fileName">Filename</param>
        /// <returns></returns>
        public CloudBlockBlob DownloadBlobAttributes(string fileName)
        {
            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName);
            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

            if (blob.Exists())
            {
                blob.FetchAttributes();
                return blob;
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// Creates Blob Container with s
        /// </summary>
        /// <param name="containerName">Name of he container</param>
        /// <param name="isPrivate">Set new container as private. True=private or False=public</param>
        private void CreateContainer(String containerName, Boolean isPrivate = true)
        {

            if (IsBlobContainerNameValid(containerName.ToLower()) == false)
            {
                throw new ArgumentException("Not a valid Blob Container Name");
            }

            // Create the blob client.
            _cloudClient = GetBlobClient();

            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(containerName.ToLower());

            // Create the container if it doesn't already exist.
            container.CreateIfNotExists();

            if (isPrivate == false)
            {
                container.SetPermissions(
                    new BlobContainerPermissions
                    {
                        PublicAccess = BlobContainerPublicAccessType.Blob
                    });
            }
        }

        /// <summary>
        /// Get a Blob Client
        /// </summary>
        /// <returns></returns>
        private CloudBlobClient GetBlobClient()
        {
            if (_storageAccount == null)
                throw new ArgumentNullException("CloudStorageAccount can not be null");

            return _storageAccount.CreateCloudBlobClient();
        }

        /// <summary>
        /// Regular expression to validate a blob container name
        /// </summary>
        /// <param name="name">Blob name to validate</param>
        /// <returns>True if valid name, False if not valid name</returns>
        public static bool IsBlobContainerNameValid(string name)
        {
            if (name.Equals("$root"))
            {
                return true;
            }
            string validBlobContainerNameRegex = @"^[a-z0-9](([a-z0-9\-[^\-])){1,61}[a-z0-9]$";
            Regex reg = new Regex(validBlobContainerNameRegex);
            if (reg.IsMatch(name))
            {
                return true;
            }
            return false;
        }

        #endregion

        #region Helper Methods
        /// <summary>
        /// Checks is a file name exists in Azure Container
        /// </summary>
        /// <param name="fileName">Name of the file to be checked</param>
        /// <returns>True if file exists, False if not</returns>
        public bool FileExist(string fileName)
        {
            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName);
            // Retrieve reference to a blob
            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
            return blob.Exists();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="guid">Lock Token GUID</param>
        /// <returns>Blob Lock File if exists</returns>
        public CloudBlockBlob GetBlobLockInfoByGuid(string guid)
        {
            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName);
            CloudBlockBlob blob = container.ListBlobs().Where(c => c.GetType() == typeof(Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob)
            && ((Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob)c).Name.Contains(guid)
            && ((Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob)c).Name.EndsWith(".locked")).Cast<CloudBlockBlob>().FirstOrDefault();

            return blob;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName">Filename</param>
        /// <returns>Blob Lock File if exists<</returns>
        public CloudBlockBlob GetBlobLockInfoByFileName(string fileName)
        {
            // Retrieve a reference to a container. 
            CloudBlobContainer container = _cloudClient.GetContainerReference(_containerName);
            CloudBlockBlob blob = container.ListBlobs().Where(c => c.GetType() == typeof(Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob)
            && ((Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob)c).Name.EndsWith(".locked")
            && ((Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob)c).Name.Contains(fileName)).Cast<CloudBlockBlob>().FirstOrDefault();

            return blob;
        }

        #endregion
    }
}
